package core

import (
	"batchEvm/storage"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/params"
	"github.com/holiman/uint256"
)

type BatchEvm struct {
	//Context负责提供该EVM Contract执行的时候所需要的上下文信息
	ConstantContext ConstantContext //不会随着Call指令而改变的Context

	ChainRules params.Rules
	Depth      int ////EVM调用栈的深度，每次Interpreter.Run就会导致Depth+1

	BatchStorage *storage.BatchStorage
	Interpreter  *EVMInterpreter

	CallGasTemp uint64
}

type EVMParam struct {
	Block       BlockContext
	Transaction TxContext
	Depth       int
	Interpreter *EVMInterpreter
}

// func NewBatchEVM(param EVMParam) *BatchEvm {
// 	evm := &BatchEvm{
// 		ConstantContext: ConstantContext{
// 			BlockContext: param.Block,
// 			TxContext:    param.Transaction,
// 		},
// 		Depth: 0,
// 		BatchStorage: ,

// 	}
// 	return evm
// }

//实现EVM四种CALL
func (evm *BatchEvm) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
	//检测调用深度
	if evm.Depth > int(params.CallCreateDepth) {
		return nil, gas, ErrDepth
	}
	//判断现在的账户中是否有足够的资金进行转账
	if value.Sign() != 0 && evm.BatchStorage.CanTransfer(caller.Address(), value) {
		return nil, gas, ErrInsufficientBalance
	}

	//对当前的ExcuteCache做一个copy
	CopyExecuteCache := evm.BatchStorage.ExecuteCache.Copy()

	//TODO：检测要调用的目标地址是否是一个预编译地址

	//先转账
	evm.BatchStorage.Transfer(caller.Address(), addr, value)

	//获取目标账户合约代码
	code := evm.BatchStorage.GetCode(addr)
	if len(code) == 0 {
		ret, err = nil, nil
	} else {
		addrCopy := addr
		//Call进行的是使用目标合约的Code修改目标合约的状态，因此在创建的时候的时候Contract.Self被设置为AccountRef(addrCopy)
		contract := NewContract(caller, AccountRef(addrCopy), value, gas)
		codeHash := evm.BatchStorage.GetCodeHash(addrCopy)
		contract.SetCallCode(&addrCopy, codeHash, code)
		ret, err = evm.Interpreter.Run(contract, input, false)
		gas = contract.Gas
	}

	// 如果执行失败就回复到之前的ExecuteCache
	if err != nil {
		evm.BatchStorage.RevertToExecuteCache(CopyExecuteCache)
		if err != ErrExecutionReverted {
			gas = 0
		}
	}
	return ret, gas, err
}

func (evm *BatchEvm) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
	//检测调用深度
	if evm.Depth > int(params.CallCreateDepth) {
		return nil, gas, ErrDepth
	}
	//判断现在的账户中是否有足够的资金进行转账
	if value.Sign() != 0 && evm.BatchStorage.CanTransfer(caller.Address(), value) {
		return nil, gas, ErrInsufficientBalance
	}
	//Tips：需要说明的是CallCode并不会转账，但是会有value这个参数，这可能是最初设计导致的历史遗留问题，因此之后发布了delegateCall

	//对当前的ExcuteCache做一个copy
	CopyExecuteCache := evm.BatchStorage.ExecuteCache.Copy()

	//TODO：检测要调用的目标地址是否是一个预编译地址
	{
		addrCopy := addr
		//CallCode进行的是用target的code修改Caller的状态，因此在创建的时候Contract。Self被设置为AccountRef(caller.Address())
		contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
		codeHash := evm.BatchStorage.GetCodeHash(addrCopy)
		code := evm.BatchStorage.GetCode(addr)
		contract.SetCallCode(&addrCopy, codeHash, code)
		ret, err = evm.Interpreter.Run(contract, input, false)
		gas = contract.Gas
	}

	// 如果执行失败就回复到之前的ExecuteCache
	if err != nil {
		evm.BatchStorage.RevertToExecuteCache(CopyExecuteCache)
		if err != ErrExecutionReverted {
			gas = 0
		}
	}
	return ret, gas, err
}

func (evm *BatchEvm) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
	//检测调用深度
	if evm.Depth > int(params.CallCreateDepth) {
		return nil, gas, ErrDepth
	}
	//对当前的ExcuteCache做一个copy
	CopyExecuteCache := evm.BatchStorage.ExecuteCache.Copy()

	//TODO：检测要调用的目标地址是否是一个预编译地址
	{
		addrCopy := addr
		//DelegateCall是CallCode的改进版，主要差别是取消了value，并且将parent。CallerAddress设置为caller的parent.CallerAddress
		contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
		codeHash := evm.BatchStorage.GetCodeHash(addrCopy)
		code := evm.BatchStorage.GetCode(addr)
		contract.SetCallCode(&addrCopy, codeHash, code)
		ret, err = evm.Interpreter.Run(contract, input, false)
		gas = contract.Gas
	}

	// 如果执行失败就回复到之前的ExecuteCache
	if err != nil {
		evm.BatchStorage.RevertToExecuteCache(CopyExecuteCache)
		if err != ErrExecutionReverted {
			gas = 0
		}
	}
	return ret, gas, err
}

func (evm *BatchEvm) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
	//检测调用深度
	if evm.Depth > int(params.CallCreateDepth) {
		return nil, gas, ErrDepth
	}

	//对当前的ExcuteCache做一个copy
	CopyExecuteCache := evm.BatchStorage.ExecuteCache.Copy()

	//给账户余额加个0，说是为了后续的改动考虑？？
	// evm.BatchStorage.AddBalance(addr, uint256.NewInt(0))

	//TODO：检测要调用的目标地址是否是一个预编译地址

	{
		addrCopy := addr
		contract := NewContract(caller, AccountRef(addrCopy), uint256.NewInt(0), gas)
		contract.SetCallCode(&addrCopy, evm.BatchStorage.GetCodeHash(addrCopy), evm.BatchStorage.GetCode(addrCopy))
		ret, err = evm.Interpreter.Run(contract, input, true)
		gas = contract.Gas
	}

	if err != nil {
		evm.BatchStorage.RevertToExecuteCache(CopyExecuteCache)
		if err != ErrExecutionReverted {
			gas = 0
		}
	}
	return ret, gas, err
}

func (evm *BatchEvm) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
	contractAddr = crypto.CreateAddress(caller.Address(), evm.BatchStorage.GetNonce(caller.Address()))
	return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}

func (evm *BatchEvm) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
	codeAndHash := &codeAndHash{code: code}
	contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
	return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}

func (evm *BatchEvm) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
	//检查调用深度
	if evm.Depth > int(params.CallCreateDepth) {
		return nil, common.Address{}, gas, ErrDepth
	}
	//检查是否能付得起
	if !evm.BatchStorage.CanTransfer(caller.Address(), value) {
		return nil, common.Address{}, gas, ErrInsufficientBalance
	}
	//对现有的ExecuteCache做一个备份
	CopyExecuteCache := evm.BatchStorage.ExecuteCache.Copy()

	//将调用者的Nonce+1
	nonce := evm.BatchStorage.GetNonce(caller.Address())
	if nonce+1 < nonce {
		return nil, common.Address{}, gas, ErrNonceUintOverflow
	}
	evm.BatchStorage.SetNonce(caller.Address(), nonce+1)
	//TODO:对于柏林分叉进行适配，加上AccessList

	//检查需要创建的target address是否已经存在合约了
	contractHash := evm.BatchStorage.GetCodeHash(address)
	if evm.BatchStorage.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
		return nil, common.Address{}, 0, ErrContractAddressCollision
	}

	//创建新账户
	var newNonce uint64
	if evm.ChainRules.IsEIP158 {
		newNonce = 1 //Tips: 个人理解这个的意思是说在EIP158之前创建的账户nonce从0开始，之后的从1开始
	}
	evm.BatchStorage.CreateAccount(address, newNonce)

	//给新账户转账
	evm.BatchStorage.Transfer(caller.Address(), address, value)

	//执行创建的合约的代码
	contract := NewContract(caller, AccountRef(address), value, gas)
	contract.SetCodeOptionalHash(&address, codeAndHash)

	//运行代码
	ret, err := evm.Interpreter.Run(contract, nil, false)

	// 防止codesize超了，合约账户的codesize被设置成了24KB
	if err == nil && evm.ChainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
		err = ErrMaxCodeSizeExceeded
	}

	if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.ChainRules.IsLondon {
		err = ErrInvalidCode
	}

	// 判断是否能付得起这么多gas
	if err == nil {
		createDataGas := uint64(len(ret)) * params.CreateDataGas
		if contract.UseGas(createDataGas) {
			evm.BatchStorage.SetNewCode(address, ret)
		} else {
			err = ErrCodeStoreOutOfGas
		}
	}

	// When an error was returned by the EVM or when setting the creation code
	// above we revert to the snapshot and consume any gas remaining. Additionally
	// when we're in homestead this also counts for code storage gas errors.
	if err != nil && (evm.ChainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
		evm.BatchStorage.RevertToExecuteCache(CopyExecuteCache)
		if err != ErrExecutionReverted {
			contract.UseGas(contract.Gas)
		}
	}

	return ret, address, contract.Gas, err
}
